/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.web.filters;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.NestedServletException;
import org.tianyun.cloud.exception.ExceptionContextAware;
import org.tianyun.cloud.exception.NestedException;
import org.tianyun.cloud.utils.JsonUtils;
import org.tianyun.cloud.web.logger.LogPrinter;
import org.tianyun.cloud.web.logger.LogPrinter.Log;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * 异常处理过滤器
 *
 * @auther ebert_chan
 */
public class ExceptionFilter extends AbstractFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionFilter.class);

    private ExceptionContextAware exceptionHandler;

    private LogPrinter logPrinter;

    public ExceptionFilter(ExceptionContextAware exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
    }

    /*
     * @see org.springframework.core.Ordered#getOrder()
     */
    @Override
    public int getOrder() {
        return FILTER_EXCEPTION_ORDERED;
    }

    /*
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            // 往下传递
            chain.doFilter(request, response);
        } catch (Throwable e) {
            // 异常反馈
            this.feedback(e, response);
        }
    }

    /**
     * 出现异常时返回JSON数据
     *
     * @param throwable
     * @param response
     * @auther ebert_chan
     */
    public void feedback(Throwable throwable, ServletResponse response) {
        while (throwable instanceof NestedServletException || throwable instanceof NestedException) {
            throwable = throwable.getCause();
        }
        LOGGER.error("Caused by:", throwable);

        ExceptionContextAware exceptionHandler = this.getExceptionHandler();
        Object result = exceptionHandler.feedback(throwable);

        if (getLogPrinter() != null) {
            Log log = getLogPrinter().getLog();
            if (log == null) {
                LOGGER.info("Out Parameters: {}", result == null ? "null" : JsonUtils.toJsonString(result));
            } else {
                getLogPrinter().writeMethodOutParameters(result);
            }
        }

        this.responseJson(result, response);
    }

    public ExceptionContextAware getExceptionHandler() {
        return exceptionHandler;
    }

    public void setExceptionHandler(ExceptionContextAware exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
    }

    public LogPrinter getLogPrinter() {
        return logPrinter;
    }

    public void setLogPrinter(LogPrinter logPrinter) {
        this.logPrinter = logPrinter;
    }

}
